<?php
/**
 * Classe Batiment de l'univers 1. Limite les éléments des groupes à 1 ainsi que le nombre de construction selon la race.
 * @author jonathan <jo.link.noir@gmail.com>
 * @version 1.3: jo_link_noir
 * @date 06-07-2010
 */
class U1_Building extends SW_Structure_Observer_Single
{
	/** @var int */
	const TYPE = 0;

	/** @var string */
	const INDEX = 'building';

	/** @var string */
	const CLASS_ELEMENT = 'U1_Building_Element';

	/** @var string */
	const CLASS_EXCEPTION = 'U1_Building_Exception';


	/** @var int : Temps de construction réduit pour les elements*/
	protected $reduceTime;

	/** @var bool : permet de chargé l'objet ressources lors de la fin d'une construction d'une mine*/
	private $existsResource = false;


	/** @var array : contient les messages d'erreur*/
	public $errorMessages = array(
		0 => 'Erreur bâtiment',
		1 => 'L\'élément doit être du type de ',
		2 => 'Bâtiment non terminé',
		3 => 'Bâtiment non débloqué',
		4 => 'Impossible de construire le bâtiment',
		5 => 'Impossible d\'arrêter la construction du bâtiment',
		6 => 'Impossible de terminé le bâtiment',
		7 => 'Bâtiment déjà en construction',
		8 => 'Bâtiment inexistante',
		9 => 'Liste du bâtiment inexistante',
		//100 -> SW_Structure_Single
		200 => 'Trop de bâtiment en construction',
		201 => 'Impossible de construire les mêmes bâtiments',
	);


	/**
	 * @param SW_Player $player
	 * @param int $idPlanet
	 */
	public function __construct(SW_Player $player, $idPlanet)
	{
		parent::__construct($player, $idPlanet, U1_Building_Dependency::factory(self::INDEX), U1_Building_DAO::getFactory(), self::CLASS_ELEMENT, self::CLASS_EXCEPTION);
	}

	/**
	 * @param SW_Structure_Element $element
	 * @return #field
	 * @exception U1_Building_Exception : Bâtiment inexistant
	 */
	protected function initElement(SW_Structure_Element $element)
	{
		$cost = self::cost($element->getGroup(), $element->getLevel(), $this->player->getRace());
		if (NULL !== $cost)
		{
			$duration = self::duration($element->getGroup(), $element->getLevel(), $this->player->getRace());
			if (NULL !== $duration)
			{
				$element->setPrice($cost);
				$element->setDuration(intval($duration / $this->reduceTime));
				return $this;
			}
		}

		throw $this->classException($this->errorMessages[8], 8);
	}

	/**
	 * @param int $id
	 * @param int $level
	 * @param int $race
	 * @return array|null
	 */
	static public function cost($id, $x, $race)
	{
		if (0 === $id)
		{
			$tmp = pow($x,5) * 3600 + 500000 * $x;
			$a = array(
				0 => $tmp,
				1 => $tmp,
				2 => pow($x+1, 4) * 3000 + 200000	* $x,
				3 => pow($x+1, 3) * 3000 + 50000	* $x
			);
		}
		else if (1 === $id)
		{
			$a = array(
				0 => (1+$x) * (25001 + 1200 / (1+$x)),
				1 => (1+$x) * (22001 + 15000 / (1+$x))
			);
		}
		else if (2 === $id)
		{
			$a = array(
				0 => pow(1.38, $x)	* (3331 + 1245	/ (1+$x)),
				1 => pow(1.4, $x)	* (2916 + 982.5	/ (1+$x))
			);
		}
		else if (3 === $id)
		{
			$tmp = pow($x,3);
			$a = array(
				0 => $tmp * 7.33+ $x * 25 + 150,
				1 => $tmp * 5	+ $x * 40 + 100
			);
		}
		else if (4 === $id)
		{
			$tmp = pow($x,3);
			$a = array(
				0 => $tmp * 8.33 + $x * 40 + 175,
				1 => $tmp * 6.25 + $x * 70 + 125
			);
		}
		else if (5 === $id)
		{
			$a = array(
				0 => pow(1.33, $x) * (600 + 60 / (1+$x)),
				1 => pow(1.26, $x) * (550 + 75 / (1+$x))
			);
		}
		else if (6 === $id)
		{
			$a = array(
				0 => pow(1.52, $x)	* 8000,
				1 => pow(1.50, $x)	* 6000,
				2 => pow(1.2, $x)	* 100 * $x,
				3 => pow(1.19, $x-1)* 50 * $x
			);
		}
		else if (7 === $id)
		{
			$a = array(
				0 => ($x+1) * (10000 + 15000 * $x),
				1 => ($x+1) * (2000 + 3000 * $x),
				2 => ($x/5) * 1000 * $x / (1+$x),
				3 => ($x/10)* 100 * $x / (1+$x)
			);
		}
		else if (8 === $id)
		{
			$a = array(
				0 => 500 + $x * (500+300*$x),
				1 => 500 + $x * (500+225*$x)
			);
		}
		else if (9 === $id)
		{
			$a = array(
				0 => pow(1.4,$x) * (40000+12000*$x),
				1 => pow(1.3,$x) * (15000+18000*$x)
			);
		}
		else if (10 === $id)
		{
			$a = array(
				0 => 5000 * pow(1.5, $x),
				1 => 4000 * pow(1.39, $x),
				2 => sqrt(0.4*$x)* floor(pow($x,1.2)*105),
				3 => sqrt(0.2*$x)* floor(pow($x,1.1)*100)
			);
		}
		else if (11 === $id)
		{
			$a = array(
				0 => $x * (6000+750*$x),
				1 => $x * (3000+1500*$x)
			);
		}
		else if (12 === $id)
		{
			$a = array(
				0 => 30000 * pow(1.3, $x),
				1 => 40000 * pow(1.2, $x)
			);
		}
		else if (13 === $id)
		{
			$tmp = 10000 * pow(1.4, $x);
			$a = array(
				0 => $tmp,
				1 => $tmp * 6
			);
		}
		else
			return null;

		//condition pour les races et arrondi du prix
		if (U1_Race::getIndex('werlingz') === $race)
			foreach ($a as &$v)
				$v = (int)($v * 1.2);
		else if (U1_Race::getIndex('yukron') === $race)
			foreach ($a as &$v)
				$v = (int)($v * 1.1);
		else
			foreach ($a as &$v)
				$v = (int)$v;

		return $a;
	}

	/**
	 * @param int $id
	 * @param int $level
	 * @param int $race
	 * @return int|null
	 */
	static public function duration($id, $x, $race)
	{
		if (0 === $id)		$d = pow($x,4) * 10	+ $x * 80000+ 200000;
		else if (1 === $id)	$d = pow($x,4) * 7	+ $x * 1000	+ 3600;
		else if (2 === $id)	$d = pow($x,3) * 5.5+ $x * 2000	+ 1800;
		else if (3 === $id)	$d = pow($x,3) * 6	+ $x * 200	+ 360;
		else if (4 === $id)	$d = pow($x,4) / 3	+ $x * 700	+ 420;
		else if (5 === $id)	$d = pow($x,4) / 2	+ $x * 1000	+ 720;
		else if (6 === $id)	$d = pow($x,5) / 5	+ $x * 800	+ 840;
		else if (7 === $id)	$d = pow($x,2) * 80	+ $x * 900	+ 3600;
		else if (8 === $id)	$d = pow($x,2) * 8	+ $x * 600	+ 900;
		else if (9 === $id)	$d = pow($x,2) * 5	+ $x * 3000	+ 7200;
		else if (10 === $id)$d = pow($x,2) * 6	+ $x * 3500	+ 9000;
		else if (11 === $id)$d = pow($x,2) * 10	+ $x * 2000	+ 5400;
		else if (12 === $id)$d = pow($x,2) * 90	+ $x * 5000	+ 9600;
		else if (13 === $id)$d = pow($x,2) * 80	+ $x * 6000	+ 12000;
		else return null;

		return (int)((U1_Race::getIndex('werlingz') === $race)
			? $d * 1.1
			: $d
		);
	}

	/**
	 * Calcule reduceTime, la durée à diviser au batiment
	 * @return float
	 */
	public function calculateReduceTime()
	{
		return 1.7
			+ pow($this->getLevelSumGroup(0), 2.5)
			+ pow($this->getLevelSumGroup(1)/2, 2);
			+ pow($this->getSiblingInstance(U1_Research::INDEX)->getLevelMaxGroup(10)/3, 1.1);
	}

	/**
	 * Initialise les dépendances, calcule le reduceTime, initialise prix et la durée et ajoute des observateur. Retourne l'id des éléments créé. Créé les éléments obligatorie s'il y en a.
	 */
	protected function _init()
	{
		$this->reduceTime = $this->calculateReduceTime();
		$this->attachOnGroup(0, $this)
			->attachOnGroup(1, $this)
			->getSiblingInstance(U1_Research::INDEX)
				->attachOnGroup(10, $this);

		parent::_init();
	}

	/**
	 * Est lancer quand un obervateur et déclencher
	 * @param SW_I_Element_Subject $subject
	 */
	protected function _update(SW_I_Element_Subject $subject)
	{
		if ($subject instanceof U1_Building_Element && (0 === $subject->getGroup() || 1 === $subject->getGroup())
		|| $subject instanceof U1_Research_Element && 10 === $subject->getGroup())
		{
			$reduce_time = $this->reduceTime;
			$this->reduceTime = $this->calculateReduceTime();
			$reduce_time = $this->reduceTime / $reduce_time;

			foreach ($this->elements as $element)
			{
				$element->setDuration(intval($element->getDuration() / $reduce_time));
			}
		}

		if ($subject instanceof U1_Building_Element)
		{
			$this->initElement($subject);
		}
	}

	/**
	 * Retourne true lorsque la structure peut construire un élément. Mais ne vérifie pas qu'un élément est constructible (rôle de elementCanBuild).
	 * @param int $timestamp
	 * @return bool|int : si int alors code d'erreur dans $errorMessages (true|200)
	 */
	public function structureCanBuild($timestamp)
	{
		//des constructions en cours
		$count_list = count($this->lists);
		return (0 !== $count_list
			&& (
				//si différent de werlingz, impossible de construire plus
				U1_Race::getIndex('werlingz') !== $this->player->getRace()
				//si werlingz est plus d'une construction, impossible de construire
			 || 1 !== $count_list
			)
		) ? 200 : parent::structureCanBuild($timestamp);
	}

	/**
	 * Retourne true si l'élément peut être construit. Mais ne vérifie pas que la structure peut construire (voir structureCanBuild)
	 * @param SW_Structure_Element $element
	 * @param int $timestamp
	 * @return bool|int : si int alors code d'erreur dans $errorMessages (true|201)
	 * @important ne vérifie pas que $element fait partit de la classe
	 */
	public function elementCanBuild(SW_Structure_Element $element, $timestamp)
	{
		//impossible de construire les mêmes bâtiment
		return isset($this->structureLists[$element->getId()]) ? 201 : parent::elementCanBuild($element, $timestamp);
	}

	/**
	 * @return array : {group_resource  => quantity, ...}
	 */
	public function getProtectedResources(array $quantites = null)
	{
		$level = $this->getLevelSumGroup(11);
		$r3 = $level * $level * 15;
		$r2 = $r3 * 10;
		$r1 = $r2 * 10 + 5000;
		return array($r1, $r1, $r2 + 1000, $r3 + 100);
	}

	/**
	 * Fini la construction de la structure demander
	 * @param SW_Structure_List $list
	 * @param int $timestamp
	 * @exception U1_Building_Exception $this->classException
	 */
	protected function _finish(SW_Structure_List $list, $timestamp)
	{
		//si une mine évolue on va d'abord appellé les ressources dans le cas où l'objet ne soit pas instancier
		if (!$this->existsResource)
		{
// 			$this->getSiblingInstance(U1_Resource::INDEX);
// 			$this->existsResource = true;
			$planet = $this->player->offsetGet(SW_Planet::INDEX);
			if (null === $planet->getInstanceIfExists(U1_Resource::INDEX, $this->idPlanet))
			{
				$group = $this->elements[$list->getIdElement()]->getGroup();
				if (3 <= $group && $group <= 6)
				{
					$planet->getInstance(U1_Resource::INDEX, $this->idPlanet);
					$this->existsResource = true;
				}
			}
			else
				$this->existsResource = true;
		}

		$this->player->addFinishElement($this, $list);
		parent::_finish($list, $timestamp);
	}

	public function __toString()
	{
		return self::INDEX;
	}
}